#ifndef __IM_CONNECTION_H__
#define __IM_CONNECTION_H__

#include <im_base_types.h>
#include <im_object.h>
#include <im_event.h>
#include <im_io_channel.h>

/**
 * @defgroup IMConnection
 * @ingroup PrivateIMBusIME
 * @{
 */

/** @struct IMConnection
 *  @brief Class to manage connection between imbus server and client.
 *
 *  The purpose of IMConnection is to handle object(event) sending/receiving
 *  between imbus server and client.
 */

IM_BEGIN_DECLS

/** @brief cast the pointer to IMConnection*, return NULL if failed. */
#define IM_CONNECTION(p)             (IM_TYPE_INSTANCE_CHECK_CAST((p),IM_TYPE_CONNECTION,IMConnection))
/** @brief cast the pointer to const IMConnection*, return NULL if failed. */
#define IM_CONST_CONNECTION(p)       (IM_TYPE_INSTANCE_CHECK_CAST_CONST((p),IM_TYPE_CONNECTION,IMConnection))
/** @brief cast the pointer to IMConnectionClass*, return NULL if failed. */
#define IM_CONNECTION_CLASS(c)       (IM_TYPE_CLASS_CHECK_CAST((c),IM_TYPE_CONNECTION,IMConnectionClass))
/** @brief tell if the pointer is an instance of IMConnection. */
#define IM_IS_CONNECTION(p)          (IM_TYPE_INSTANCE_CHECK_TYPE((p),IM_TYPE_CONNECTION))
/** @brief tell if the pointer is IMConnectionClass. */
#define IM_IS_CONNECTION_CLASS(c)    (IM_TYPE_CLASS_CHECK_TYPE((c),IM_TYPE_CONNECTION))
/** @brief get the type id from an instance of IMConnection. */
#define IM_CONNECTION_GET_CLASS(p)   (IM_TYPE_INSTANCE_GET_CLASS_CAST((p),IM_TYPE_CONNECTION,IMConnectionClass))

typedef struct _IMConnection                    IMConnection;
typedef struct _IMConnectionClass               IMConnectionClass;

/**
 * @brief Create a new IMConnection object from an opened IMIOChannel.
 *
 * The newly created IMConnection object is owned by the creator
 * (not floating). So creator should unref it explicitly when it's not
 * used anymore. And it'll own a reference of the specified IMIOChannel
 * object, so the creator should unref the IOChannel object, if it owned
 * by creator and won't be used by creator directly anymore.
 */
IMConnection*   im_connection_new               (IMIOChannel        *channel);

/**
 * @brief Create a new IMConnection object and connect to a specified address.
 *
 * The newly created IMConnection object is owned by the creator
 * (not floating). So creator should unref it explicitly when it's not
 * used anymore.
 *
 * @param address pointer to the address string.
 * @return A newly created IMConnection object on success. If fail to create
 * the new object or fail to connect to the specified address, then a null
 * pointer is returned.
 */
IMConnection*   im_connection_new_connect       (const IMChar       *address,
                                                 IMIOError          *error);

/**
 * @brief Check if an IMConnection object is actually connected.
 */
IMBool          im_connection_is_connected      (const IMConnection *connection);

/**
 * @brief Close a connected IMConnection object.
 *
 * The object itself won't be destroyed by this function.
 */
void            im_connection_close             (IMConnection       *connection);

/**
 * @brief Return the IMIOChannel object used by a specified IMConnection
 *        object.
 *
 * The IMIOChannel object is owned by the IMConnection object, don't unref
 * it in caller.
 */
IMIOChannel*    im_connection_get_io_channel    (const IMConnection *connection);

/**
 * @brief Let an IMConnection object to use a specified IMIOchannel object.
 *
 * A reference of the IMIOChannel object will be occupied by the IMConnection
 * object, so caller should unref the IMIOChannel object afterwards.
 */
IMBool          im_connection_set_io_channel    (IMConnection       *connection,
                                                 IMIOChannel        *channel);

/**
 * @brief Check if there is any pending object in incoming queue.
 *
 * If there is no object in queue, then try to read one or more objects
 * from channel without blocking.
 *
 * @return >  0 The number of pending objects in queue.
 *         == 0 No object in queue and can't read any object from io channel.
 *         <  0 Error occurred, error number will be stored in error.
 */
IMInt           im_connection_pending           (IMConnection       *connection,
                                                 IMIOError          *error);
/**
 * @brief Return the next object in the queue.
 *
 * If there is no object in queue, then try to read some from io channel
 * within specified timeout.
 *
 * @param timeout, A specified timeout value (in milliseconds) used for
 *        reading from io channel. When return, the remained timeout value
 *        will be stored in it. -1 means unlimited.
 *
 * @return An object with one reference, it should be unreferenced by caller.
 *         Or NULL if failed to read an object within specified timeout, or
 *         some error occurred. Check the value of *error to see whether its
 *         an error or just timeout.
 */
IMPointer       im_connection_next              (IMConnection       *connection,
                                                 IMInt              *timeout,
                                                 IMIOError          *error);

/**
 * @brief Send an object over the io channel.
 *
 * @param timeout, A specified timeout value (in milliseconds) used for
 *        writing to io channel. When return, the remained timeout value
 *        will be stored in it. -1 means unlimited.
 */
IMBool          im_connection_send              (IMConnection       *connection,
                                                 IMConstPointer      object,
                                                 IMInt              *timeout,
                                                 IMIOError          *error);

IM_END_DECLS

#endif
/**  @} */

/*
vi:ts=4:nowrap:ai:expandtab
*/
